#pragma once 
#include <iostream>
#include <pthread.h>
#include <queue>
#include <time.h>


namespace my_blockq 
{
  template<class T>
  class BlockQueue{
    public:
      BlockQueue(int capacity):_capacity(capacity){
        pthread_mutex_init(&_mux, nullptr);
        pthread_cond_init(&_full, nullptr);
        pthread_cond_init(&_empty, nullptr);
      }
      ~BlockQueue(){
        pthread_mutex_destroy(&_mux);
        pthread_cond_destroy(&_full);
        pthread_cond_destroy(&_empty);
      }
    public:
      void LockQueue(){ 
        pthread_mutex_lock(&_mux);
      }

      void UnlockQueue(){
        pthread_mutex_unlock(&_mux);
      }

      void producter_wait(){
        //首先会释放锁，然后将所在线程挂起，返回时会主动竞争锁，获取到锁后，才能返回
        pthread_cond_wait(&_full, &_mux);
      }
      
      void consumer_wait(){
        pthread_cond_wait(&_empty, &_mux);
      }

      void consumer_wakeup(){
        pthread_cond_signal(&_full);
      }
      
      void producter_wakeup(){
        pthread_cond_signal(&_empty);
      }
      
      void Push(const T& in){
        //向队列中放入数据
        LockQueue();
        while (is_full()){
          producter_wait();
        }
        _bq.push(in);
        consumer_wakeup();
        UnlockQueue();
      }

      void Pop(T* out){
        //向队列中读取数据
        LockQueue();
        while (is_empty()){
          consumer_wait();
        }
        *out = _bq.front();
        _bq.pop();
        producter_wakeup();
        UnlockQueue();
      }

    private:
      bool is_full(){
        return _bq.size() == _capacity;
      }

      bool is_empty(){
        return _bq.empty();
      }
    private:
      std::queue<T> _bq;   //队列
      int _capacity;       //容量
      pthread_mutex_t _mux; //保护临界资源的锁
      //1.当生产满了就不要生产了(不要竞争锁了),应该让消费者进行消费
      //2.当消费空了，就不要再消费了(不要再竞争锁了), 应该让生产者生产
      pthread_cond_t _full;  //bq满了
      pthread_cond_t _empty; //bq空了
  };
}
